给CardLib添加深度复制

  下面把上述内容付诸于实践:使用ICloneable接口,复制Card、Cards和Deck对象,这在某些扑克牌游戏中是有用的,因为在这些游戏中不需要让两副扑克牌引用同一组Card对象,但肯定会使一副扑克牌中的牌序与另一副牌的牌序相同。

  在Ch11CardLib中,对Card类执行复制操作是很简单的,因为只需进行浅度复制(Card只包含值类型的数据,其形式为字段)。我们只需对类定义进行如下修改:

    public class Card : ICloneable
    {
        public object Clone()
        {
            return MemberwiseClone();
        }
    }

  ICloneable接口的这段实现代码只是一个浅度复制,无法确定在Clone()方法中执行了什么操作,所以这已经足以满足我们的需求。

  接着,需要对Cards集合类实现ICloneable接口。这个过程稍复杂些,因为涉及到复制源集合中的每个Card对象,所以需要进行深度复制:

    public class Cards : CollectionBase, ICloneable
    {
        public object Clone()
        {
            Cards newCards = new Cards();
            foreach(Card sourceCard in List)
            {
                newCards.Add((Card)sourceCard.Clone());
            }
            return newCards;
        }
    }

  最后,需要在Deck类上实现ICloneable接口。这里存在一个小问题:因为Ch11CardLib中的Deck类无法修改它包含的扑克牌,所以没有洗牌。例如,无法修改有给定牌序的Deck实例。为解决这个问题,为Deck类定义一个新的私有构造函数,在实例化Deck对象时,可以给该函数传送指定的Cards集合。所以,在这个类中执行复制的代码如下所示:

    public class Deck : ICloneable
    {
        public object Clone()
        {
            Deck newDeck = new Deck(cards.Clone() as Cards);
            return newDeck;
        }

        private Deck(Cards newCards)
        {
            cards = newCards;
        }
    }

  再次用一些简单的客户代码进行测试。与以前一样,这应放在客户项目的Main()方法中,以便进行测试(包含在本章下载代码的Ch11CardClient\Program.cs文件中):

    Deck deck1 = new Deck();
    Deck deck2 = (Deck)deck1.Clone();
    Console.WriteLine("The first card in the original deck is : {0}",
                      deck1.GetCard(0));
    Console.WriteLine("The first card in the cloned deck is : {0}",
                      deck2.GetCard(0));
    deck1.Shuffle();
    Console.WriteLine("Original deck shuffled.");
    Console.WriteLine("The first card in the original deck is : {0}",
                      deck1.GetCard(0));
    Console.WriteLine("The first card in the cloned deck is : {0}",
                      deck2.GetCard(0));
    Console.ReadKey();

🔚